package com.yeskery.nut.application.netty;

import com.yeskery.nut.websocket.Session;
import com.yeskery.nut.websocket.WebSocketConfiguration;
import com.yeskery.nut.websocket.netty.NettyWebSocketSession;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.websocketx.*;

/**
 * Netty WebSocket服务处理器
 * @author sprout
 * @version 1.0
 * 2023-04-22 16:29
 */
@ChannelHandler.Sharable
public class NettyWebSocketHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    /** 请求参数解码器 */
    private final QueryStringDecoder decoder;

    /** WebSocket配置对象 */
    private final WebSocketConfiguration webSocketConfiguration;

    /**
     * 构建Netty WebSocket服务处理器
     * @param decoder 请求参数解码器
     * @param webSocketConfiguration WebSocket配置对象
     */
    public NettyWebSocketHandler(QueryStringDecoder decoder, WebSocketConfiguration webSocketConfiguration) {
        this.decoder = decoder;
        this.webSocketConfiguration = webSocketConfiguration;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception {
        handleWebSocketFrame(ctx, msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        Session session = new NettyWebSocketSession(ctx, decoder, webSocketConfiguration.getWebSocketServerConfigure());
        webSocketConfiguration.getWebSocketHandler().onError(session, cause);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Session session = new NettyWebSocketSession(ctx, decoder, webSocketConfiguration.getWebSocketServerConfigure());
        webSocketConfiguration.getWebSocketHandler().onClose(session);
    }

    /**
     * 处理websocket窗口
     * @param ctx 通道处理器上下文
     * @param frame websocket窗口
     */
    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
        if (frame instanceof TextWebSocketFrame) {
            Session session = new NettyWebSocketSession(ctx, decoder, webSocketConfiguration.getWebSocketServerConfigure());
            webSocketConfiguration.getWebSocketHandler().onMessage(((TextWebSocketFrame) frame).text(), session);
            return;
        }
        if (frame instanceof PingWebSocketFrame) {
            ctx.writeAndFlush(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        if (frame instanceof CloseWebSocketFrame) {
            ctx.writeAndFlush(frame.retainedDuplicate()).addListener(ChannelFutureListener.CLOSE);
            return;
        }
        if (frame instanceof BinaryWebSocketFrame) {
            Session session = new NettyWebSocketSession(ctx, decoder, webSocketConfiguration.getWebSocketServerConfigure());
            ByteBuf contentByteBuf = frame.content();
            byte[] bytes = new byte[contentByteBuf.readableBytes()];
            contentByteBuf.readBytes(bytes);
            webSocketConfiguration.getWebSocketHandler().onMessage(bytes, session);
        }
    }
}
